<?php

/*
 * Copyright 2008 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#namespace tehframework\events;

#use \Closure;

class EventHandler implements Countable
{
	/**
	 * @var array
	 */
	private $listeners = array();
	
	/**
	 * @param  array<callback> $listeners
	 */
	public function __construct(array $listeners = null)
	{
		if (!empty($listeners))
		{
			foreach ($listeners as $listener)
			{
				$this->addListener($listener);
			}
		}
	}
	
	/**
	 * @return integer
	 */
	public function count()
	{
		return count($this->listeners);
	}
	
	/**
	 * Determines whether the specified listener is already registered.
	 * 
	 * @param  callback $callback
	 *         A listener to check whether it is already registered.
	 * @return boolean
	 *         TRUE if specified listener exists, otherwise FALSE.
	 */
	public function hasListener($callback)
	{
		return in_array($callback, $this->listeners, true);
	}
	
	/**
	 * Adds the specified listener at the top of the listeners stack.
	 * 
	 * @param  callback $callback
	 *         A listener to register.
	 * @return EventHandler
	 */
	public function addListener($callback)
	{
		$this->listeners[] = $callback;
		
		return $this;
	}
	
	/**
	 * Removes the first encountered callback of specified type.
	 *
	 * @param  callback $callback
	 *         A callback to remove.
	 * @return EventHandler
	 */
	public function removeListener($callback)
	{
		$key = array_search($callback, $this->listeners, true);
		
		if ($key !== false)
		{
			unset($this->listeners[$key]);
		}
		
		return $this;
	}
	
	/**
	 * Removes all registered callbacks of specified type.
	 *
	 * @param  callback $callback
	 *         A callback to remove.
	 * @return EventHandler
	 */
	public function removeListeners($callback)
	{
		$keys = array_keys($this->listeners, $callback, true);
		
		if (!empty($keys))
		{
			foreach ($keys as $key)
			{
				unset($this->listeners[$key]);
			}
		}
		
		return $this;
	}
	
	/**
	 * Executes all listeners with specified sender and event arguments
	 * in FIFO order.
	 * 
	 * @param  object $sender
	 *         An instance of object that fired this event.
	 * @param  EventArgs $e
	 *         Event arguments that will be passed to listeners.
	 */
	public function fireEvent($sender, EventArgs $e)
	{
		$params = array($sender, $e);
		
		foreach ($this->listeners as $listener)
		{
			if ($listener instanceof Closure)
			{
				$listener($sender, $e);
			}
			else
			{
				call_user_func_array($listener, $params);
			}
		}
	}
}